package com.virtudroid.util;

import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import android.util.Log;

import com.virtudroid.domain.WavHeaderInfo;

/**
 * Utility class allowing to read and parse the WAV files RIFF headers.
 * 
 * @author Seb
 */
public class WavHeaderReaderUtil {
	
	private static final int HEADER_SIZE = 44;
	
	private static final int CHUNK_ID_POSITION = 0;
	private static final int CHUNK_SIZE_POSITION = 4;
	private static final int FORMAT_POSITION = 8;
	private static final int SUBCHUNK1_ID_POSITION = 12;
	private static final int SUBCHUNK1_SIZE_POSITION = 16;
	private static final int AUDIO_FORMAT_POSITION = 20;
	private static final int NUM_CHANNELS_POSITION = 22;
	private static final int SAMPLE_RATE_POSITION = 24;
	private static final int BYTE_RATE_POSITION = 28;
	private static final int BLOCK_ALIGN_POSITION = 32;
	private static final int BITS_PER_SAMPLE_POSITION = 34;
	private static final int SUBCHUNK2_ID_POSITION = 36;
	private static final int SUBCHUNK2_SIZE_POSITION = 40;
	
	private static final ByteOrder CHUNK_ID_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
	private static final ByteOrder CHUNK_SIZE_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder FORMAT_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
	private static final ByteOrder SUBCHUNK1_ID_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
	private static final ByteOrder SUBCHUNK1_SIZE_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder AUDIO_FORMAT_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder NUM_CHANNELS_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder SAMPLE_RATE_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder BYTE_RATE_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder BLOCK_ALIGN_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder BITS_PER_SAMPLE_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	private static final ByteOrder SUBCHUNK2_ID_BYTE_ORDER = ByteOrder.BIG_ENDIAN;
	private static final ByteOrder SUBCHUNK2_SIZE_BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
	
	
	private ByteBuffer byteBuffer;
	
	public WavHeaderReaderUtil(InputStream wavInputStream) {
		byteBuffer = ByteBuffer.allocate(HEADER_SIZE);
		try {
			wavInputStream.read(byteBuffer.array(), byteBuffer.arrayOffset(), byteBuffer.capacity());
		} catch (IOException e) {
			Log.e("SimpleWavReader", "Can not read wav file", e);
			// TODO handle exception
		}
	}
	
	/**
	 * Read RIFF header from the file header and converts it 
	 * into a {@link WavHeaderInfo}.
	 * @return a {@link WavHeaderInfo} representing the parsed RIFF header.
	 */
	public WavHeaderInfo readInfos() {
		int chunkId = readInt(CHUNK_ID_POSITION, CHUNK_ID_BYTE_ORDER);
		int chunkSize = readInt(CHUNK_SIZE_POSITION, CHUNK_SIZE_BYTE_ORDER);
		int format = readInt(FORMAT_POSITION, FORMAT_BYTE_ORDER);
		int subchunk1Id = readInt(SUBCHUNK1_ID_POSITION, SUBCHUNK1_ID_BYTE_ORDER);
		int subchunk1Size = readInt(SUBCHUNK1_SIZE_POSITION, SUBCHUNK1_SIZE_BYTE_ORDER);
		short audioFormat = readShort(AUDIO_FORMAT_POSITION, AUDIO_FORMAT_BYTE_ORDER);
		short numChannels = readShort(NUM_CHANNELS_POSITION, NUM_CHANNELS_BYTE_ORDER);
		int sampleRate = readInt(SAMPLE_RATE_POSITION, SAMPLE_RATE_BYTE_ORDER);
		int byteRate = readInt(BYTE_RATE_POSITION, BYTE_RATE_BYTE_ORDER);
		short blockAlign = readShort(BLOCK_ALIGN_POSITION, BLOCK_ALIGN_BYTE_ORDER);
		short bitsPerSample = readShort(BITS_PER_SAMPLE_POSITION, BITS_PER_SAMPLE_BYTE_ORDER);
		int subchunk2Id = readInt(SUBCHUNK2_ID_POSITION, SUBCHUNK2_ID_BYTE_ORDER);
		int subchunk2Size = readInt(SUBCHUNK2_SIZE_POSITION, SUBCHUNK2_SIZE_BYTE_ORDER);
		
		Log.i("WavHeaderReaderUtil", "Chunk ID : " + chunkId);
		Log.i("WavHeaderReaderUtil", "Chunk size : " + chunkSize);
		Log.i("WavHeaderReaderUtil", "Format : " + format);
		Log.i("WavHeaderReaderUtil", "Subchunk 1 ID : " + subchunk1Id);
		Log.i("WavHeaderReaderUtil", "Subchunk 1 size : " + subchunk1Size);
		Log.i("WavHeaderReaderUtil", "Audio format : " + audioFormat);
		Log.i("WavHeaderReaderUtil", "Num channels : " + numChannels);
		Log.i("WavHeaderReaderUtil", "Sample rate : " + sampleRate);
		Log.i("WavHeaderReaderUtil", "Byte rate : " + byteRate);
		Log.i("WavHeaderReaderUtil", "Block align : " + blockAlign);
		Log.i("WavHeaderReaderUtil", "Bits per sample : " + bitsPerSample);
		Log.i("WavHeaderReaderUtil", "Subchunk 2 id : " + subchunk2Id);
		Log.i("WavHeaderReaderUtil", "Subchunk 2 size : " + subchunk2Size);
		
		WavHeaderInfo result = new WavHeaderInfo();
		result.setChunkId(chunkId);
		result.setChunkSize(chunkSize);
		result.setFormat(format);
		result.setSubchunk1Id(subchunk1Id);
		result.setSubchunk1Size(subchunk1Size);
		result.setAudioFormat(audioFormat);
		result.setNumChannels(numChannels);
		result.setSampleRate(sampleRate);
		result.setByteRate(byteRate);
		result.setBlockAlign(blockAlign);
		result.setBitsPerSample(bitsPerSample);
		result.setSubchunk2Id(subchunk2Id);
		result.setSubchunk2Size(subchunk2Size);
	 
		return result;
	}
	
	/**
	 * Read an integer from the {@link ByteBuffer} containing the file header.
	 * @param startPosition the start position in the {@link ByteBuffer}.
	 * @param byteOrder the {@link ByteOrder} to use to read this integer
	 * @return the integer read
	 */
	private int readInt(int startPosition, ByteOrder byteOrder) {
		byteBuffer.order(byteOrder);
		return byteBuffer.getInt(startPosition);
	}
	
	/**
	 * Read an short from the {@link ByteBuffer} containing the file header.
	 * @param startPosition the start position in the {@link ByteBuffer}.
	 * @param byteOrder the {@link ByteOrder} to use to read this integer
	 * @return the short read
	 */
	private short readShort(int startPosition, ByteOrder byteOrder) {
		byteBuffer.order(byteOrder);
		return byteBuffer.getShort(startPosition);
	}
}
